/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package NN;

import java.nio.ByteBuffer;
import java.util.Random;

/**
 *
 * @author ollo
 */
public class Neuron {
    protected static Random rand = new Random(System.currentTimeMillis());
    protected static final double WRANGE = 1000;
    protected double err, weightedInputs;
    protected double[] weights, inputs;
    protected Neuron[] outputs;
    private int nInLayer;

    public Neuron(int nIn, int nInLayer, Neuron[] outputs){
        weights = new double[nIn+1];
        inputs = new double[nIn];
        this.nInLayer = nInLayer;
        this.outputs = outputs;
        for (int i = 0; i < weights.length; i++) {
            weights[i] = rand.nextDouble()*10 -5;            
        }        
    }
    
    void activate() {
        calcWeightedInputs();
        double signal = Sigmoid.Sig(weightedInputs);
        for (Neuron n : outputs) {
            n.recieveInput(signal, nInLayer);
        }
    }

    void calcWeightedInputs() {
        weightedInputs = 0;
        for (int i = 0; i < inputs.length; i++) {
            weightedInputs += weights[i] * inputs[i];
        }
        weightedInputs += weights[weights.length-1];
    }
    
    void recieveInput(double input, int from) {
        inputs[from] = input;
    }
    
    void recieveInput(double inputs[]) {
        this.inputs = inputs;
    }

    // Backpropagation functions

    double getErr(int n) {
        return err * weights[n];
    }

    void calcNodeErr() {
        err = 0;
        for (int i = 0; i < outputs.length; i++) {
            err += outputs[i].getErr(i);
        }
    }
    
    void adjustWeights(double learning) {
        double signal = Sigmoid.Sig(weightedInputs);
        int i;
        for (i = 0; i < weights.length-1; i++) {
            weights[i] += learning * inputs[i] * err * Sigmoid.SigPrim(signal);
        }
        weights[i] += learning * err * Sigmoid.SigPrim(signal);
    }

    //GA functions
    void writeBinaryGenome(ByteBuffer buffer) {        
        for (double d : weights) {
            buffer.putInt(doubleToInt(d));
        }              
    }
    
    void readBinaryGenome(ByteBuffer buffer) {        
        for (int i = 0; i < weights.length; i++) {
            weights[i] = intToDouble(buffer.getInt());            
        }        
    }   
    
    private int doubleToInt(double val) {
        return (int)(val * Integer.MAX_VALUE / WRANGE); 
    }
    
    private double intToDouble(int ival) {        
        return ival * WRANGE / Integer.MAX_VALUE;        
    }
    
    int getNWeights() {
        return weights.length;
    }

  /*  abstract void calcNodeErr();
    
    abstract 
    abstract void adjustWeights(double learning);*/

}
